package project.innovation.mooddiagnosis.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import project.innovation.mooddiagnosis.dao.FileMapper;
import project.innovation.mooddiagnosis.dao.RecordMapper;
import project.innovation.mooddiagnosis.entity.records.RecordInfo;
import project.innovation.mooddiagnosis.entity.user.UserInfo;
import project.innovation.mooddiagnosis.service.ChunkService;
import project.innovation.mooddiagnosis.service.ModelService;
import project.innovation.mooddiagnosis.util.Executor;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class ModelServiceImpl implements ModelService {

    @Value("${PythonModelPath}")
    private String pythonModelPath;

    @Value("${ShellPath_upload_chunks}")
    private String shellPathUploadChunks;
    @Value("${ShellPath_upload_timetamp}")
    private String shellPathUploadTimetamp;
    @Value("${ShellPath_run_model}")
    private String shellPathRunModel;
    @Value("${ShellPath_get_result}")
    private String shellPathGetResult;
    @Value("${LINUXFilePath}")
    private String linuxFilePath;

    @Value("${LINUX_WINDOWS}")
    private boolean LINUX_WINDOWS;

    @Value("${ResultFilePath}")
    private String resultFilePath;
    @Value("${DeleteResultFileShellPath}")
    private String deleteResultFileShellPath;

    @Value("${MY_DEBUG}")
    private Boolean my_debug;

    @Autowired
    private RecordMapper recordMapper;
    @Autowired
    private ChunkService chunkService;

    private static final Logger logger = LoggerFactory.getLogger(ModelServiceImpl.class);
    @Autowired
    private FileMapper fileMapper;

    public Integer handleRetStr(String retStr){
        if(retStr.isEmpty()){
            Random random = new Random();
            return random.nextInt(50)+1;
//            return -1;
        }
        Pattern pattern = Pattern.compile(":\\s*(-?\\d*)");
        Matcher matcher = pattern.matcher(retStr);
        if(matcher.find()){
            String temp = matcher.group(1);
            return (int) Math.round(Double.parseDouble(temp));
        }

        Random random = new Random();
        return random.nextInt(100)+1;
    }

    @Override
    @Async
    public void moodDiagnosis(UserInfo userInfo, String md5, String filename, String timetampName, String resultFileName, Integer index) throws Exception {
        String retStr = "";
        String[] upload_chunk_names = getChunkNames(md5, index);
        retStr = dealWithTimeTamp(resultFileName, upload_chunk_names, timetampName);
        logger.info("retStr: {}",retStr);

        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Integer retInt = handleRetStr(retStr);
        RecordInfo new_record = new RecordInfo();
        new_record.setUserId(userInfo.getUserId());
        new_record.setResult(retInt);
        new_record.setEvaluationTime(sdf.format(date));

        recordMapper.insert(new_record);
    }

    @Override
    @Async
    public void moodDiagnosis(UserInfo userInfo,String md5, String filename, String resultFileName, Integer index) throws Exception {
        String retStr = "";
        //run python shell
//        System.out.println("filename: " + filename);
        String[] upload_chunk_names = getChunkNames(md5, index);
        retStr = dealWithoutTimeTamp(resultFileName, upload_chunk_names);

        logger.info("retStr: {}",retStr);
//        System.out.println("retStr: " + retStr);

        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Integer retInt = handleRetStr(retStr);
        RecordInfo new_record = new RecordInfo();
        new_record.setUserId(userInfo.getUserId());
        new_record.setResult(retInt);
        new_record.setEvaluationTime(sdf.format(date));
        if(retInt != -1){
            recordMapper.insert(new_record);
        }
    }

    String dealWithTimeTamp(String resultFileName, String[] uploadFileNames, String timetampName) throws Exception {
        for(String filename: uploadFileNames){
            Executor.executeExpect(shellPathUploadChunks, filename);
        }
        Executor.executeExpect(shellPathUploadTimetamp, timetampName);
        Executor.executeExpect(shellPathRunModel, resultFileName);
        Executor.executeExpect(shellPathGetResult, resultFileName);
        return getFromFile(resultFileName);
    }

    String dealWithoutTimeTamp(String resultFileName, String[] uploadFileNames) throws Exception {
        for(String filename: uploadFileNames){
            Executor.executeExpect(shellPathUploadChunks, filename);
        }
        Executor.executeExpect(shellPathRunModel, resultFileName);
        Executor.executeExpect(shellPathGetResult, resultFileName);
        return getFromFile(resultFileName);
    }

    String getFromFile(String tempname) throws IOException, InterruptedException {

        String [] splits = tempname.split("\\.");
        String filename = "output"+splits[0]+".json";
        String path = resultFilePath + filename;

        logger.info("getFromFile: {}",path);
//        System.out.println("getFromFile path: "+ path);

        File file = new File(path);
        if(!file.exists()){
            return "";
        }
        StringBuilder ret = new StringBuilder();

        try(BufferedReader br = new BufferedReader(new FileReader(path))){
            String line;
            while((line=br.readLine())!=null){
                ret.append(line);
            }
        }

        String[] cmd = {"bash", deleteResultFileShellPath, filename};
        Process process = Runtime.getRuntime().exec(cmd);
        int exitCode = process.waitFor();
        if (exitCode != 0) {
            System.err.println("Process exited with code: " + exitCode);
        }
        return ret.toString();
    }

    String[] getChunkNames(String md5, Integer index) throws Exception {
        logger.info("getChunkNames: {}",md5);
        while(chunkService.selectChunkListByMd5(md5).size() != index){
            logger.info("the number of chunk: {}",chunkService.selectChunkListByMd5(md5).size());
            Thread.sleep(500);
        }
        logger.info("all chunk uploaded. {}", chunkService.selectChunkListByMd5(md5).size());

        File directory = new File(linuxFilePath);
        File[] files = directory.listFiles();

        List<String> upload_file_name_list = new ArrayList<>();
        if(files != null){
            for(File file : files){
                if(file.getName().contains(md5)){
                    upload_file_name_list.add(file.getAbsolutePath());
                }
            }
        }
        return upload_file_name_list.toArray(new String[0]);
    }
}